-module(server).
-export([start/0]).

-import(werkzeug,[get_config_value/2,logging/2,logstop/0,timeMilliSecond/0,to_String/1, reset_timer/3]).
-import(queuemanager,[start/2]).

%einstieg ins Programm, communicateWithClient(1):1 = erste MsgId
start() ->			initialize().
%initialisiert den Server durch das laden der server.cfg					
initialize() -> {ok, ConfigListe} = file:consult("server.cfg"),
			    {ok, Lifetime} = werkzeug:get_config_value(lifetime, ConfigListe),
			    {ok, Clientlifetime} = werkzeug:get_config_value(clientlifetime, ConfigListe),
			    {ok, Servername} = werkzeug:get_config_value(servername, ConfigListe),
			    {ok, DlqLimit} = werkzeug:get_config_value(dlqlimit, ConfigListe),
			    register(Servername,self()),
				ServerPID = self(),
				QueueManPID = spawn(fun() -> queuemanager:start(Servername, DlqLimit,ServerPID) end),
			    {ok,Timer} = timer:send_after(Lifetime*1000, self(), 'feierAbend'),
			    comunicateWithClient(1, Servername, Timer, Lifetime, Clientlifetime,QueueManPID, []).
				
%kommunikation und weiterleitung zwischen client und Server; und Server	und Queuemanager		
comunicateWithClient(ID, Servername, Timer, Lifetime, Clientlifetime, QueueMan, Clientliste) 	-> 
				%Clientanfrage auf eine neue ID, wird vom server selber verwaltet. Sendet die MsgId an Client, resettet den LifeTimer des Servers
				% und resettet den ClientTimer.
	receive		{getmsgid, PID} ->  TimerNeu = werkzeug:reset_timer(Timer, Lifetime, 'feierAbend'),
									PID ! {nnr, ID},
									ClientlisteNeu = refreshClient(PID, Clientliste, Clientlifetime),
									writeToLog(Servername,lists:concat(["Nachrichtennummer ", werkzeug:to_String(ID)," an ", werkzeug:to_String(PID)," gesendet."])),
									comunicateWithClient(ID+1, Servername, TimerNeu, Lifetime, Clientlifetime,QueueMan, ClientlisteNeu);
				%Clientanfrage auf Msgs, wird an Queuemanager weiterdeligiert, resettet den LifeTimer des Servers
				% und resettet den ClientTimer.
				{getmessages, PID} -> TimerNeu = werkzeug:reset_timer(Timer, Lifetime, 'feierAbend'),
									  ClientlisteNeu = refreshClient(PID, Clientliste, Clientlifetime),
									  Nummer = nextClientMsgNumber(PID, Clientliste),
									  QueueMan ! {getmessages,PID,Nummer},
									  comunicateWithClient(ID,Servername,TimerNeu,Lifetime, Clientlifetime, QueueMan, ClientlisteNeu);
				%Client Nachrichteneingang delegiert weiter an den Queuemanager und resettet den LifeTimer des Servers
				% und resettet den ClientTimer.
				{dropmessage, {Nachricht, Nummer}} -> TimerNeu = werkzeug:reset_timer(Timer, Lifetime, 'feierAbend'),
													  QueueMan ! {dropmessage,{Nachricht,Nummer}},
													  comunicateWithClient(ID, Servername, TimerNeu, Lifetime, Clientlifetime,QueueMan, Clientliste);	
				%reagiert auf Timerablauf des Clients in der Clientliste und l�scht diesen aus der Liste
				{deleteClient, PID}    -> ClientlisteNeu = deleteClient(PID,Clientliste),
									   writeToLog(Servername,lists:concat([werkzeug:to_String(PID)," vergessen."])),
								           comunicateWithClient(ID,Servername,Timer,Lifetime, Clientlifetime,QueueMan, ClientlisteNeu);
				%Queuemanager Nachricht, wird an den Client durch den Server weitergeleitet.
				{queueman,PID,{Number,Nachricht,Terminated}} ->  						PID ! {reply,Number,Nachricht,Terminated},
																writeToLog(Servername, lists:concat([Nachricht,"-reply an Client"])),
																if
																Number /= 0 -> ClientlisteNeu = setNextMsgNumberForClient(PID, Number+1, Clientliste);
																true -> ClientlisteNeu=Clientliste
																end,
																comunicateWithClient(ID,Servername,Timer,Lifetime, Clientlifetime,QueueMan, ClientlisteNeu);
				%Server beendet sich.
				feierAbend		->  	QueueMan ! 'feierAbend',
								unregister(wk),
								writeToLog(Servername,"Server beendet sich."),
								werkzeug:logstop(),
								bye
	end.
						  
writeToLog(Servername, Text) -> werkzeug:logging(lists:concat([Servername,".log"]), lists:concat([Text,"\r\n"])).
%Setzt den Timer des Clients zur�ck und gibt die neue Clientliste zur�ck
refreshClient(PID,Clientliste, Clientlifetime) -> {ClientPID, NextMsgNumber, Timer} = searchClientEntry(PID,Clientliste),
												  if
												  {ClientPID, NextMsgNumber, Timer} /= {0,0,0} -> 	TimerNeu = werkzeug:reset_timer(Timer, Clientlifetime, {deleteClient,PID}),
																			ClientlisteNeu = lists:delete({ClientPID, NextMsgNumber, Timer},Clientliste),
																			lists:append([{ClientPID, NextMsgNumber, TimerNeu}], ClientlisteNeu);
												  true -> addClient(PID,Clientliste,Clientlifetime)
												  end.
												  
%l�scht den Client aus der Clientliste und gibt die neue Clientliste zur�ck												  
deleteClient(PID, Clientliste) -> 	{ClientPID, NextMsgNumber, Timer} = searchClientEntry(PID,Clientliste),
									lists:delete({ClientPID, NextMsgNumber, Timer}, Clientliste).
%sucht die als n�chstes an den Client zu sendende Nachricht raus und gibt die nummer zur�ck			
nextClientMsgNumber(PID, Clientliste) -> {_CPID, NextMsgNumber,_Timer} = searchClientEntry(PID, Clientliste),
									     NextMsgNumber.

%Setzt die Nummer die der Client als n�chstes zu bekommen hat in der Clientliste, gibt die neue Liste zur�ck
setNextMsgNumberForClient(PID, Number, Clientliste) -> {ClientPID, NextMsgNumber,Timer} = searchClientEntry(PID,Clientliste),
										ClientlisteNeu = lists:delete({ClientPID, NextMsgNumber, Timer},Clientliste),
										lists:append([{PID, Number, Timer}], ClientlisteNeu).
										  
										  
									
%f�gt einen neuen Client der Clientliste hinzu
addClient(PID,Clientliste, Clientlifetime) -> {ok,Timer} = timer:send_after(Clientlifetime*1000, self(), {deleteClient, PID}),
											  Triple = {PID, 1, Timer},
											  lists:append([Triple], Clientliste).
											  
%durchsucht die Clientliste nach dem Triple zu der PID und gibt das Triple zur�ck.
searchClientEntry(PID, Clientliste) ->  searchClientEntryR(PID, Clientliste).

searchClientEntryR(_, []) -> {0,0,0};
searchClientEntryR(PID, [{ClientListHeadPID, MsNr, TimerPID}|ClientListTail]) ->
								if
									PID ==  ClientListHeadPID 	-> {ClientListHeadPID, MsNr, TimerPID};
									true 						-> searchClientEntryR(PID, ClientListTail)
								end.
				
				
				
				

